﻿using CTM.Web.Models.Input;
using CTM.Web.Models.View.Report;
using CTM.Web.Services;
using Microsoft.AspNet.Identity;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;

namespace CTM.Web.Utils
{
    public static class ApplicationHelpers
    {
        public static string IsActive(this HtmlHelper html, string actions = "", string controllers = "", string cssClass = "active")
        {
            ViewContext viewContext = html.ViewContext;
            bool isChildAction = viewContext.Controller.ControllerContext.IsChildAction;

            if (isChildAction)
                viewContext = html.ViewContext.ParentActionViewContext;

            RouteValueDictionary routeValues = viewContext.RouteData.Values;
            string currentAction = routeValues["action"].ToString();
            string currentController = routeValues["controller"].ToString();

            if (String.IsNullOrEmpty(actions))
                actions = currentAction;

            if (String.IsNullOrEmpty(controllers))
                controllers = currentController;

            string[] acceptedActions = actions.Trim().Split(',').Distinct().ToArray();
            string[] acceptedControllers = controllers.Trim().Split(',').Distinct().ToArray();

            return acceptedActions.Contains(currentAction) && acceptedControllers.Contains(currentController) ? cssClass : String.Empty;
        }

        public static bool IsManager(IAccountService accountService, ApplicationUserManager userManager, string currentUserId, string selectedUserId)
        {
            if (accountService == null || currentUserId == null || selectedUserId == null)
                return false;

            var currentUser = accountService.GetById(currentUserId);
            var selectedUser = accountService.GetById(selectedUserId);
            if (currentUser != null && selectedUser != null)
            {
                // if Employee1 is HR
                var currentUserRoles = userManager.GetRoles(currentUser.Id);
                if (currentUserRoles.Contains("HR"))
                    return true;

                // is direct manager
                if (selectedUser.Manager != null && selectedUser.Manager.Id == currentUser.Id)
                    return true;

                if (currentUser.Id != selectedUser.Id && selectedUser.Org != null && currentUser.Org != null && selectedUser.Org.Path.StartsWith(currentUser.Org.Path))
                    return true;
            }

            return false;
        }

        public static string GetDescription(this Enum value)
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());
            DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
            if (attributes != null && attributes.Length > 0) return attributes[0].Description;
            else return value.ToString();
        }

        //public static bool IsManager(IAccountService accountService, IOrgService orgService, string managerId, string userId)
        //{
        //    var manager = accountService.GetById(managerId);
        //    if (manager == null) throw new Exception("Manager does not exist.");

        //    var user = accountService.GetById(userId);
        //    if (user == null) throw new Exception("User does not exist.");

        //    if (managerId == userId) return false;
        //    if (user.Manager != null && user.Manager.Id == managerId) return true;

        //    var managerOrgs = orgService.GetByManager(managerId, true).Select(o=>o.Id);
        //    if (user.Org != null && managerOrgs.Contains(user.Org.Id))
        //        return true;

        //    return false;
        //}

        public static bool IsReportViewer(IAccountService accountService, ApplicationUserManager userManager, string currentUserId)
        {
            if (accountService == null || currentUserId == null)
                return false;

            var currentUser = accountService.GetById(currentUserId);
            if (currentUser != null)
            {
                // if Employee1 is HR
                var currentUserRoles = userManager.GetRoles(currentUser.Id);
                if (currentUserRoles.Contains("HR"))
                    return true;

                return currentUser.Employees.Any() ? true : currentUser.ManageOrgs.Any();
            }

            return false;
        }

        public static bool IsHR(ApplicationUserManager userManager, string userId)
        {
            var currentUserRoles = userManager.GetRoles(userId);
            return currentUserRoles.Contains("HR");
        }

        public static DateTime ConvertToLocalTime(DateTime whatever)
        {
            DateTime iKnowThisIsUtc = whatever;
            DateTime runtimeKnowsThisIsUtc = DateTime.SpecifyKind(iKnowThisIsUtc, DateTimeKind.Utc);
            DateTime localVersion = runtimeKnowsThisIsUtc.ToLocalTime();
            return localVersion;
        }

        public static List<TreeNode> BuildTree(IAccountService accountService, ApplicationUserManager userManager, IOrgService orgService, string userId, out List<Guid> allOrgIds)
        {
            var allOrgs = new List<TreeNode>();
            allOrgIds = new List<Guid>();
            var tree = new List<TreeNode>();
            if (accountService == null || orgService == null || string.IsNullOrEmpty(userId)) return tree;

            var currentUser = accountService.GetById(userId);
            if (currentUser == null) return tree;

            Org currentOrg = null;
            TreeNode root = null;
            if(ApplicationHelpers.IsHR(userManager, userId))
            {
                allOrgs = orgService.Get(o => o.IsDeleted == false).Select(o => new TreeNode
                    {
                        id = o.Id,
                        text = o.Name,
                        parentId = o.ParentId
                    }).ToList();
                root = new TreeNode
                {
                    id = Guid.Empty,
                    text = "RBEI",
                    parentId = null
                };
            }
            else
            {
                currentOrg = currentUser.Org == null ?
                orgService.Get(o => o.Type.Name == "Global Entity" && o.Parent == null).FirstOrDefault() :
                orgService.GetById(currentUser.Org.Id);
                if (currentOrg != null)
                {
                    allOrgs = orgService.Get(o => o.IsDeleted == false && o.Path.Contains(currentOrg.Path)).Select(o => new TreeNode
                        {
                            id = o.Id,
                            text = o.Name,
                            parentId = o.ParentId
                        }).ToList();

                    root = new TreeNode
                    {
                        id = currentOrg.Id,
                        text = currentOrg.Name,
                        parentId = currentOrg.ParentId
                    };
                }
            }

            if (root == null) return tree;

            BuildSubTree(root, allOrgs);
            tree.Add(root);
            allOrgIds = allOrgs.Select(o => o.id).ToList();
            /*
            allOrgIds.Add(root.id);
            var currentNode = new TreeNode
            {
                id = currentOrg.Id,
                text = currentOrg.Name
            };
            foreach (var child in currentOrg.Children)
            {
                var childNode = new TreeNode
                {
                    id = child.Id,
                    text = child.Name
                };

                allOrgIds.Add(child.Id);
                BuildSubTree(childNode, child, ref allOrgIds);

                if (currentNode.nodes == null)
                    currentNode.nodes = new List<TreeNode> { childNode };
                else
                    currentNode.nodes.Add(childNode);
            }

            tree.Add(currentNode);
            */
            return tree;
        }

        private static void BuildSubTree(TreeNode currentNode, List<TreeNode> allOrgs)
        {
            if(currentNode.id == Guid.Empty)
            {
                currentNode.nodes = allOrgs.Where(o=>o.parentId.HasValue == false).OrderBy(o=>o.text).ToList();
            }
            else
            {
                currentNode.nodes = allOrgs.Where(o => o.parentId.HasValue && o.parentId.Value == currentNode.id).OrderBy(o => o.text).ToList();
            }

            foreach (var node in currentNode.nodes)
            {
                BuildSubTree(node, allOrgs);
            }
        }

        private static void BuildSubTree(TreeNode currentNode, Org currentOrg, ref List<Guid> allOrgs)
        {
            foreach (var child in currentOrg.Children)
            {
                var childNode = new TreeNode
                {
                    id = child.Id,
                    text = child.Name
                };

                allOrgs.Add(child.Id);

                BuildSubTree(childNode, child, ref allOrgs);

                if (currentNode.nodes == null)
                    currentNode.nodes = new List<TreeNode> { childNode };
                else
                    currentNode.nodes.Add(childNode);
            }
        }

        public static List<Guid> GetLowerOrgs(IAccountService accountService, IOrgService orgService, string userId)
        {
            var orgs = new List<Guid>();
            if (orgService == null || string.IsNullOrEmpty(userId)) return orgs;

            var currentUser = accountService.GetById(userId);
            if (currentUser == null) return orgs;

            var currentOrg = currentUser.Org == null ?
                orgService.Get(o => o.Type.Name == "Global Entity" && o.Parent == null).FirstOrDefault() :
                orgService.GetById(currentUser.Org.Id);

            if (currentOrg == null) return orgs;

            orgs.Add(currentOrg.Id);
            GetSubOrgs(orgs, currentOrg);

            return orgs;
        }

        private static void GetSubOrgs(IList<Guid> orgs, Org currentOrg)
        {
            foreach (var child in currentOrg.Children)
            {
                orgs.Add(child.Id);
                GetSubOrgs(orgs, child);
            }
        }

        public static string RenderRazorViewToString(this Controller controller, string viewName, object model)
        {
            controller.ViewData.Model = model;
            using (var sw = new StringWriter())
            {
                var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
                var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
                viewResult.View.Render(viewContext, sw);
                viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
                return sw.GetStringBuilder().ToString();
            }
        }

        public static string ToMessageAndCompleteStacktrace(this Exception exception)
        {
            Exception e = exception;
            var s = new StringBuilder();
            while (e != null)
            {
                var st = new StackTrace(e, true);
                var frame = st.GetFrame(0);
                var line = frame.GetFileLineNumber();

                s.AppendLine();
                s.AppendLine("Exception type: " + e.GetType().FullName);
                s.AppendLine("Message       : " + e.Message);
                s.AppendLine("Stacktrace:");
                s.AppendLine(e.StackTrace);
                s.AppendLine();
                e = e.InnerException;
            }
            return s.ToString();
        }

        public static string GetAllExceptionInfo(this Exception ex)
        {
            StringBuilder sbexception = new StringBuilder();

            int i = 1;
            sbexception.Append(GetExceptionInfo(ex, i));

            while (ex.InnerException != null)
            {
                i++;
                ex = ex.InnerException;
                sbexception.Append(GetExceptionInfo(ex, i));
            }

            return sbexception.ToString();
        }

        private static string GetExceptionInfo(Exception ex, int count)
        {
            StringBuilder sbexception = new StringBuilder();
            sbexception.AppendLine(string.Format(""));
            sbexception.AppendLine(string.Format(""));
            sbexception.AppendLine(string.Format("************************************************"));
            sbexception.AppendLine(string.Format("************************************************"));
            sbexception.AppendLine(string.Format(" Inner Exception : No.{0} ", count));
            sbexception.AppendLine(string.Format("************************************************"));
            sbexception.AppendLine(string.Format("=================================================="));
            sbexception.AppendLine(string.Format(" Error Message : {0} ", ex.Message));
            sbexception.AppendLine(string.Format("=================================================="));
            #region Mine Thru data dictionary

            try
            {
                sbexception.AppendLine(string.Format("=================================================="));
                sbexception.AppendLine(string.Format(" Data parameters Count at Source :{0}", ex.Data.Count));
                sbexception.AppendLine(string.Format("=================================================="));

                string skey = string.Empty;
                foreach (object key in ex.Data.Keys)
                {
                    try
                    {
                        if (key != null)
                        {
                            skey = Convert.ToString(key);
                            sbexception.AppendLine(string.Format(" Key :{0} , Value:{1}", skey, Convert.ToString(ex.Data[key])));
                        }
                        else
                        {
                            sbexception.AppendLine(string.Format(" Key is null"));
                        }
                    }
                    catch (Exception e1)
                    {
                        sbexception.AppendLine(string.Format("**  Exception occurred when writting log *** [{0}] ", e1.Message));
                    }
                }
            }
            catch (Exception ex1)
            {
                sbexception.AppendLine(string.Format("**  Exception occurred when writting log *** [{0}] ", ex1.Message));
            }

            #endregion
            sbexception.AppendLine(string.Format("=================================================="));
            sbexception.AppendLine(string.Format(" Source : {0} ", ex.Source));
            sbexception.AppendLine(string.Format("=================================================="));
            sbexception.AppendLine(string.Format(" StackTrace : {0} ", ex.StackTrace));
            sbexception.AppendLine(string.Format("=================================================="));
            sbexception.AppendLine(string.Format(" TargetSite : {0} ", ex.TargetSite));
            sbexception.AppendLine(string.Format("************************************************"));
            sbexception.AppendLine(string.Format(" Finished Writting Exception info :{0} ", count));
            sbexception.AppendLine(string.Format("************************************************"));
            sbexception.AppendLine(string.Format("************************************************"));
            sbexception.AppendLine(string.Format(""));
            sbexception.AppendLine(string.Format(""));

            return sbexception.ToString();
        }
    }
}